home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevpdfm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  52.3 KB  |  1,761 lines

  1. /* Copyright (C) 1996, 2000 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gdevpdfm.c,v 1.7 2000/09/19 19:00:17 lpd Exp $ */
  20. /* pdfmark processing for PDF-writing driver */
  21. #include "math_.h"
  22. #include "memory_.h"
  23. #include "string_.h"
  24. #include "gx.h"
  25. #include "gserrors.h"
  26. #include "gsutil.h"        /* for bytes_compare */
  27. #include "gdevpdfx.h"
  28. #include "gdevpdfo.h"
  29. #include "scanchar.h"
  30.  
  31. /* GC descriptors */
  32. private_st_pdf_article();
  33. private_st_pdf_graphics_save();
  34.  
  35. /*
  36.  * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark
  37.  * operator in the input file.  Its "value" is the arguments of the operator,
  38.  * passed through essentially unchanged:
  39.  *      (key, value)*, CTM, type
  40.  */
  41.  
  42. /*
  43.  * Define an entry in a table of pdfmark-processing procedures.
  44.  * (The actual table is at the end of this file, to avoid the need for
  45.  * forward declarations for the procedures.)
  46.  */
  47. #define PDFMARK_NAMEABLE 1    /* allows _objdef */
  48. #define PDFMARK_ODD_OK 2    /* OK if odd # of parameters */
  49. #define PDFMARK_KEEP_NAME 4    /* don't substitute reference for name */
  50.                 /* in 1st argument */
  51. #define PDFMARK_NO_REFS 8    /* don't substitute references for names */
  52.                 /* anywhere */
  53. typedef struct pdfmark_name_s {
  54.     const char *mname;
  55.     pdfmark_proc((*proc));
  56.     byte options;
  57. } pdfmark_name;
  58.  
  59. /* ---------------- Public utilities ---------------- */
  60.  
  61. /* Compare a C string and a gs_param_string. */
  62. bool
  63. pdf_key_eq(const gs_param_string * pcs, const char *str)
  64. {
  65.     return (strlen(str) == pcs->size &&
  66.         !strncmp(str, (const char *)pcs->data, pcs->size));
  67. }
  68.  
  69. /* Scan an integer out of a parameter string. */
  70. int
  71. pdfmark_scan_int(const gs_param_string * pstr, int *pvalue)
  72. {
  73. #define max_int_str 20
  74.     uint size = pstr->size;
  75.     char str[max_int_str + 1];
  76.  
  77.     if (size > max_int_str)
  78.     return_error(gs_error_limitcheck);
  79.     memcpy(str, pstr->data, size);
  80.     str[size] = 0;
  81.     return (sscanf(str, "%d", pvalue) == 1 ? 0 :
  82.         gs_note_error(gs_error_rangecheck));
  83. #undef max_int_str
  84. }
  85.  
  86. /* ---------------- Private utilities ---------------- */
  87.  
  88. /* Find a key in a dictionary. */
  89. private bool
  90. pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count,
  91.          gs_param_string * pstr)
  92. {
  93.     uint i;
  94.  
  95.     for (i = 0; i < count; i += 2)
  96.     if (pdf_key_eq(&pairs[i], key)) {
  97.         *pstr = pairs[i + 1];
  98.         return true;
  99.     }
  100.     pstr->data = 0;
  101.     pstr->size = 0;
  102.     return false;
  103. }
  104.  
  105. /*
  106.  * Get the page number for a page referenced by number or as /Next or /Prev.
  107.  * The result may be 0 if the page number is 0 or invalid.
  108.  */
  109. private int
  110. pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr)
  111. {
  112.     int page = pdev->next_page + 1;
  113.  
  114.     if (pnstr->data == 0);
  115.     else if (pdf_key_eq(pnstr, "/Next"))
  116.     ++page;
  117.     else if (pdf_key_eq(pnstr, "/Prev"))
  118.     --page;
  119.     else if (pdfmark_scan_int(pnstr, &page) < 0)
  120.     page = 0;
  121.     return page;
  122. }
  123.  
  124. /* Construct a destination string specified by /Page and/or /View. */
  125. /* Return 0 if none (but still fill in a default), 1 or 2 if present */
  126. /* (1 if only one of /Page or /View, 2 if both), <0 if error. */
  127. private int
  128. pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev,
  129.           const char *Page_key, const char *View_key,
  130.           const gs_param_string * pairs, uint count)
  131. {
  132.     gs_param_string page_string, view_string;
  133.     int present =
  134.     pdfmark_find_key(Page_key, pairs, count, &page_string) +
  135.     pdfmark_find_key(View_key, pairs, count, &view_string);
  136.     int page = pdfmark_page_number(pdev, &page_string);
  137.     gs_param_string action;
  138.     int len;
  139.  
  140.     if (view_string.size == 0)
  141.     param_string_from_string(view_string, "[/XYZ 0 0 1]");
  142.     if (page == 0)
  143.     strcpy(dstr, "[null ");
  144.     else if (pdfmark_find_key("/Action", pairs, count, &action) &&
  145.          pdf_key_eq(&action, "/GoToR")
  146.     )
  147.     sprintf(dstr, "[%d ", page - 1);
  148.     else
  149.     sprintf(dstr, "[%ld 0 R ", pdf_page_id(pdev, page));
  150.     len = strlen(dstr);
  151.     if (len + view_string.size > MAX_DEST_STRING)
  152.     return_error(gs_error_limitcheck);
  153.     if (view_string.data[0] != '[' ||
  154.     view_string.data[view_string.size - 1] != ']'
  155.     )
  156.     return_error(gs_error_rangecheck);
  157.     memcpy(dstr + len, view_string.data + 1, view_string.size - 1);
  158.     dstr[len + view_string.size - 1] = 0;
  159.     return present;
  160. }
  161.  
  162. /*
  163.  * If a named destination is specified by a string, convert it to a name,
  164.  * update *dstr, and return 1; otherwise return 0.
  165.  */
  166. private int
  167. pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
  168. {
  169.     const byte *data = dstr->data;
  170.     uint size = dstr->size;
  171.  
  172.     if (size == 0 || data[0] != '(')
  173.     return 0;
  174.     /****** HANDLE ESCAPES ******/
  175.     memcpy(dest, data, size - 1);
  176.     dest[0] = '/';
  177.     dest[size - 1] = 0;
  178.     dstr->data = (byte *)dest;
  179.     dstr->size = size - 1;
  180.     return 1;
  181. }
  182.  
  183. /* Put pairs in a dictionary. */
  184. private int
  185. pdfmark_put_c_pair(cos_dict_t *pcd, const char *key,
  186.            const gs_param_string * pvalue)
  187. {
  188.     return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size);
  189. }
  190. private int
  191. pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair)
  192. {
  193.     return cos_dict_put_string(pcd, pair->data, pair->size,
  194.                    pair[1].data, pair[1].size);
  195. }
  196.  
  197. /* Scan a Rect value. */
  198. private int
  199. pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str,
  200.           const gs_matrix * pctm)
  201. {
  202.     uint size = str->size;
  203.     double v[4];
  204. #define MAX_RECT_STRING 100
  205.     char chars[MAX_RECT_STRING + 3];
  206.     int end_check;
  207.  
  208.     if (str->size > MAX_RECT_STRING)
  209.     return_error(gs_error_limitcheck);
  210.     memcpy(chars, str->data, size);
  211.     strcpy(chars + size, " 0");
  212.     if (sscanf(chars, "[%lg %lg %lg %lg]%d",
  213.            &v[0], &v[1], &v[2], &v[3], &end_check) != 5
  214.     )
  215.     return_error(gs_error_rangecheck);
  216.     gs_point_transform(v[0], v[1], pctm, &prect->p);
  217.     gs_point_transform(v[2], v[3], pctm, &prect->q);
  218.     return 0;
  219. }
  220.  
  221. /* Make a Rect value. */
  222. private void
  223. pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect)
  224. {
  225.     /*
  226.      * We have to use a stream and pprintf, rather than sprintf,
  227.      * because printf formats can't express the PDF restriction son
  228.      * the form of the output.
  229.      */
  230.     stream s;
  231.  
  232.     swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1);
  233.     pprintg4(&s, "[%g %g %g %g]",
  234.          prect->p.x, prect->p.y, prect->q.x, prect->q.y);
  235.     str[stell(&s)] = 0;
  236. }
  237.  
  238. /* Write a transformed Border value on a stream. */
  239. private int
  240. pdfmark_write_border(stream *s, const gs_param_string *str,
  241.              const gs_matrix *pctm)
  242. {
  243.     /*
  244.      * We don't preserve the entire CTM in the output, and it isn't clear
  245.      * what CTM is applicable to annotations anyway: we only attempt to
  246.      * handle well-behaved CTMs here.
  247.      */
  248.     uint size = str->size;
  249. #define MAX_BORDER_STRING 100
  250.     char chars[MAX_BORDER_STRING + 1];
  251.     double bx, by, c;
  252.     gs_point bpt, cpt;
  253.     const char *next;
  254.  
  255.     if (str->size > MAX_BORDER_STRING)
  256.     return_error(gs_error_limitcheck);
  257.     memcpy(chars, str->data, size);
  258.     chars[size] = 0;
  259.     if (sscanf(chars, "[%lg %lg %lg", &bx, &by, &c) != 3)
  260.     return_error(gs_error_rangecheck);
  261.     gs_distance_transform(bx, by, pctm, &bpt);
  262.     gs_distance_transform(0.0, c, pctm, &cpt);
  263.     pprintg3(s, "[%g %g %g", fabs(bpt.x), fabs(bpt.y), fabs(cpt.x + cpt.y));
  264.     /*
  265.      * We don't attempt to do 100% reliable syntax checking here --
  266.      * it's just not worth the trouble.
  267.      */
  268.     next = strchr(chars + 1, ']');
  269.     if (next == 0)
  270.     return_error(gs_error_rangecheck);
  271.     if (next[1] != 0) {
  272.     /* Handle a dash array.  This is tiresome. */
  273.     double v;
  274.  
  275.     pputc(s, '[');
  276.     while (next != 0 && sscanf(++next, "%lg", &v) == 1) {
  277.         gs_point vpt;
  278.  
  279.         gs_distance_transform(0.0, v, pctm, &vpt);
  280.         pprintg1(s, "%g ", fabs(vpt.x + vpt.y));
  281.         next = strchr(next, ' ');
  282.     }
  283.     pputc(s, ']');
  284.     }
  285.     pputc(s, ']');
  286.     return 0;
  287. }
  288.  
  289. /* Put an element in a stream's dictionary. */
  290. private int
  291. cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value)
  292. {
  293.     return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value);
  294. }
  295.  
  296. /* ---------------- Miscellaneous pdfmarks ---------------- */
  297.  
  298. /*
  299.  * Create the dictionary for an annotation or outline.  For some
  300.  * unfathomable reason, PDF requires the following key substitutions
  301.  * relative to pdfmarks:
  302.  *   In annotation and link dictionaries:
  303.  *     /Action => /A, /Color => /C, /Title => /T
  304.  *   In outline directionaries:
  305.  *     /Action => /A, but *not* /Color or /Title
  306.  *   In Action subdictionaries:
  307.  *     /Dest => /D, /File => /F, /Subtype => /S
  308.  * and also the following substitutions:
  309.  *     /Action /Launch /File xxx =>
  310.  *       /A << /S /Launch /F xxx >>
  311.  *     /Action /GoToR /File xxx /Dest yyy =>
  312.  *       /A << /S /GoToR /F xxx /D yyy' >>
  313.  *     /Action /Article /Dest yyy =>
  314.  *       /A << /S /Thread /D yyy' >>
  315.  *     /Action /GoTo => drop the Action key
  316.  * Also, \n in Contents strings must be replaced with \r.
  317.  * Note that for Thread actions, the Dest is not a real destination,
  318.  * and must not be processed as one.
  319.  *
  320.  * We always treat /A and /F as equivalent to /Action and /File
  321.  * respectively.  The pdfmark and PDF documentation is so confused on the
  322.  * issue of when the long and short names should be used that we only give
  323.  * this a 50-50 chance of being right.
  324.  *
  325.  * Note that we must transform Rect and Border coordinates.
  326.  */
  327.  
  328. typedef struct ao_params_s {
  329.     gx_device_pdf *pdev;    /* for pdfmark_make_dest */
  330.     const char *subtype;    /* default Subtype in top-level dictionary */
  331.     long src_pg;        /* set to SrcPg - 1 if any */
  332. } ao_params_t;
  333. private int
  334. pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd,
  335.              const gs_param_string * pairs, uint count,
  336.              const gs_matrix * pctm, ao_params_t * params,
  337.              bool for_outline)
  338. {
  339.     const gs_param_string *Action = 0;
  340.     const gs_param_string *File = 0;
  341.     gs_param_string Dest;
  342.     gs_param_string Subtype;
  343.     uint i;
  344.     int code;
  345.     char dest[MAX_DEST_STRING];
  346.     bool coerce_dest = false;
  347.  
  348.     Dest.data = 0;
  349.     if (!for_outline) {
  350.     code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
  351.                  pairs, count);
  352.     if (code < 0)
  353.         return code;
  354.     else if (code == 0)
  355.         Dest.data = 0;
  356.     else
  357.         param_string_from_string(Dest, dest);
  358.     }
  359.     if (params->subtype)
  360.     param_string_from_string(Subtype, params->subtype);
  361.     else
  362.     Subtype.data = 0;
  363.     for (i = 0; i < count; i += 2) {
  364.     const gs_param_string *pair = &pairs[i];
  365.     long src_pg;
  366.  
  367.     if (pdf_key_eq(pair, "/SrcPg") &&
  368.         sscanf((const char *)pair[1].data, "%ld", &src_pg) == 1
  369.         )
  370.         params->src_pg = src_pg - 1;
  371.     else if (!for_outline && pdf_key_eq(pair, "/Color"))
  372.         pdfmark_put_c_pair(pcd, "/C", pair + 1);
  373.     else if (!for_outline && pdf_key_eq(pair, "/Title"))
  374.         pdfmark_put_c_pair(pcd, "/T", pair + 1);
  375.     else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A"))
  376.         Action = pair;
  377.     else if (pdf_key_eq(pair, "/File") || pdf_key_eq(pair, "/F"))
  378.         File = pair;
  379.     else if (pdf_key_eq(pair, "/Dest")) {
  380.         Dest = pair[1];
  381.         coerce_dest = true;
  382.     }
  383.     else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) {
  384.         /* Make a destination even if this is for an outline. */
  385.         if (Dest.data == 0) {
  386.         code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
  387.                      pairs, count);
  388.         if (code < 0)
  389.             return code;
  390.         param_string_from_string(Dest, dest);
  391.         coerce_dest = false;
  392.         }
  393.     } else if (pdf_key_eq(pair, "/Subtype"))
  394.         Subtype = pair[1];
  395.     /*
  396.      * We also have to replace all occurrences of \n in Contents
  397.      * strings with \r.  Unfortunately, they probably have already
  398.      * been converted to \012....
  399.      */
  400.     else if (pdf_key_eq(pair, "/Contents")) {
  401.         byte *cstr;
  402.         uint csize = pair[1].size;
  403.         cos_value_t *pcv;
  404.         uint i, j;
  405.  
  406.         /*
  407.          * Copy the string into value storage, then update it in place.
  408.          */
  409.         pdfmark_put_pair(pcd, pair);
  410.         /* Break const so we can update the (copied) string. */
  411.         pcv = (cos_value_t *)
  412.         cos_dict_find(pcd, (const byte *)"/Contents", 9);
  413.         cstr = pcv->contents.chars.data;
  414.         /* Loop invariant: j <= i < csize. */
  415.         for (i = j = 0; i < csize;)
  416.         if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) &&
  417.             (i == 0 || cstr[i - 1] != '\\')
  418.             ) {
  419.             cstr[j] = '\\', cstr[j + 1] = 'r';
  420.             i += 2, j += 2;
  421.         } else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) &&
  422.                (i == 0 || cstr[i - 1] != '\\')
  423.             ) {
  424.             cstr[j] = '\\', cstr[j + 1] = 'r';
  425.             i += 4, j += 2;
  426.         } else
  427.             cstr[j++] = cstr[i++];
  428.         if (j != i)
  429.         pcv->contents.chars.data =
  430.             gs_resize_string(pdev->pdf_memory, cstr, csize, j,
  431.                      "pdfmark_put_ao_pairs");
  432.     } else if (pdf_key_eq(pair, "/Rect")) {
  433.         gs_rect rect;
  434.         char rstr[MAX_RECT_STRING];
  435.         int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
  436.  
  437.         if (code < 0)
  438.         return code;
  439.         pdfmark_make_rect(rstr, &rect);
  440.         cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr,
  441.                       strlen(rstr));
  442.     } else if (pdf_key_eq(pair, "/Border")) {
  443.         stream s;
  444.         char bstr[MAX_BORDER_STRING + 1];
  445.         int code;
  446.  
  447.         swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1);
  448.         code = pdfmark_write_border(&s, pair + 1, pctm);
  449.         if (code < 0)
  450.         return code;
  451.         if (stell(&s) > MAX_BORDER_STRING)
  452.         return_error(gs_error_limitcheck);
  453.         bstr[stell(&s)] = 0;
  454.         cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr,
  455.                       strlen(bstr));
  456.     } else if (for_outline && pdf_key_eq(pair, "/Count"))
  457.         DO_NOTHING;
  458.     else
  459.         pdfmark_put_pair(pcd, pair);
  460.     }
  461.     if (!for_outline && pdf_key_eq(&Subtype, "/Link")) {
  462.     if (Action) {
  463.         /* Don't delete the Dest for GoTo or file-GoToR. */
  464.         if (pdf_key_eq(Action + 1, "/GoTo") ||
  465.         (File && pdf_key_eq(Action + 1, "/GoToR"))
  466.         )
  467.         DO_NOTHING;
  468.         else
  469.         Dest.data = 0;
  470.     }
  471.     }
  472.  
  473.     /* Now handle the deferred keys. */
  474.     if (Action) {
  475.     const byte *astr = Action[1].data;
  476.     const uint asize = Action[1].size;
  477.  
  478.     if ((File != 0 || Dest.data != 0) &&
  479.         (pdf_key_eq(Action + 1, "/Launch") ||
  480.          (pdf_key_eq(Action + 1, "/GoToR") && File) ||
  481.          pdf_key_eq(Action + 1, "/Article"))
  482.         ) {
  483.         cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
  484.         cos_value_t avalue;
  485.  
  486.         if (adict == 0)
  487.         return_error(gs_error_VMerror);
  488.         if (!for_outline) {
  489.         /* We aren't sure whether this is really needed.... */
  490.         cos_dict_put_c_strings(adict, "/Type", "/Action");
  491.         }
  492.         if (pdf_key_eq(Action + 1, "/Article")) {
  493.         cos_dict_put_c_strings(adict, "/S", "/Thread");
  494.         coerce_dest = false; /* Dest is not a real destination */
  495.         }
  496.         else
  497.         pdfmark_put_c_pair(adict, "/S", Action + 1);
  498.         if (Dest.data) {
  499.         if (coerce_dest)
  500.             pdfmark_coerce_dest(&Dest, dest);
  501.         pdfmark_put_c_pair(adict, "/D", &Dest);
  502.         Dest.data = 0;    /* so we don't write it again */
  503.         }
  504.         if (File) {
  505.         pdfmark_put_c_pair(adict, "/F", File + 1);
  506.         File = 0;    /* so we don't write it again */
  507.         }
  508.         cos_dict_put(pcd, (const byte *)"/A", 2,
  509.              COS_OBJECT_VALUE(&avalue, adict));
  510.     } else if (asize >= 4 && !memcmp(astr, "<<", 2)) {
  511.         /* Replace occurrences of /Dest, /File, and /Subtype. */
  512.         const byte *scan = astr + 2;
  513.         const byte *end = astr + asize;
  514.         gs_param_string key, value;
  515.         cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
  516.         cos_value_t avalue;
  517.         int code;
  518.  
  519.         if (adict == 0)
  520.         return_error(gs_error_VMerror);
  521.         while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) {
  522.         key.size = scan - key.data;
  523.         if (key.data[0] != '/' ||
  524.             (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1)
  525.             break;
  526.         value.size = scan - value.data;
  527.         if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) {
  528.             param_string_from_string(key, "/D");
  529.             if (value.data[0] == '(') {
  530.             /****** HANDLE ESCAPES ******/
  531.             pdfmark_coerce_dest(&value, dest);
  532.             }
  533.         } else if (pdf_key_eq(&key, "/File"))
  534.             param_string_from_string(key, "/F");
  535.         else if (pdf_key_eq(&key, "/Subtype"))
  536.             param_string_from_string(key, "/S");
  537.         cos_dict_put_string(adict, key.data, key.size,
  538.                     value.data, value.size);
  539.         }
  540.         if (code <= 0 || !pdf_key_eq(&key, ">>"))
  541.         return_error(gs_error_rangecheck);
  542.         cos_dict_put(pcd, (const byte *)"/A", 2,
  543.              COS_OBJECT_VALUE(&avalue, adict));
  544.     } else if (pdf_key_eq(Action + 1, "/GoTo"))
  545.         pdfmark_put_pair(pcd, Action);
  546.     }
  547.     /*
  548.      * If we have /Dest or /File without the right kind of action,
  549.      * simply write it at the top level.  This doesn't seem right,
  550.      * but I'm not sure what else to do.
  551.      */
  552.     if (Dest.data) {
  553.     if (coerce_dest)
  554.         pdfmark_coerce_dest(&Dest, dest);
  555.     pdfmark_put_c_pair(pcd, "/Dest", &Dest);
  556.     }
  557.     if (File)
  558.     pdfmark_put_pair(pcd, File);
  559.     if (Subtype.data)
  560.     pdfmark_put_c_pair(pcd, "/Subtype", &Subtype);
  561.     return 0;
  562. }
  563.  
  564. /* Copy an annotation dictionary. */
  565. private int
  566. pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  567.           const gs_matrix * pctm, const gs_param_string *objname,
  568.           const char *subtype)
  569. {
  570.     ao_params_t params;
  571.     cos_dict_t *pcd;
  572.     int page_index = pdev->next_page;
  573.     cos_array_t *annots;
  574.     cos_value_t value;
  575.     int code;
  576.  
  577.     params.pdev = pdev;
  578.     params.subtype = subtype;
  579.     params.src_pg = -1;
  580.     code = pdf_make_named_dict(pdev, objname, &pcd, true);
  581.     if (code < 0)
  582.     return code;
  583.     code = cos_dict_put_c_strings(pcd, "/Type", "/Annot");
  584.     if (code < 0)
  585.     return code;
  586.     code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, ¶ms, false);
  587.     if (code < 0)
  588.     return code;
  589.     if (params.src_pg >= 0)
  590.     page_index = params.src_pg;
  591.     if (pdf_page_id(pdev, page_index + 1) <= 0)
  592.     return_error(gs_error_rangecheck);
  593.     annots = pdev->pages[page_index].Annots;
  594.     if (annots == 0) {
  595.     annots = cos_array_alloc(pdev, "pdfmark_annot");
  596.     if (annots == 0)
  597.         return_error(gs_error_VMerror);
  598.     pdev->pages[page_index].Annots = annots;
  599.     }
  600.     if (!objname) {
  601.     /* Write the annotation now. */
  602.     COS_WRITE_OBJECT(pcd, pdev);
  603.     COS_RELEASE(pcd, "pdfmark_annot");
  604.     }
  605.     return cos_array_add(annots,
  606.              cos_object_value(&value, COS_OBJECT(pcd)));
  607. }
  608.  
  609. /* ANN pdfmark */
  610. private int
  611. pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  612.         const gs_matrix * pctm, const gs_param_string * objname)
  613. {
  614.     return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text");
  615. }
  616.  
  617. /* LNK pdfmark (obsolescent) */
  618. private int
  619. pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  620.         const gs_matrix * pctm, const gs_param_string * objname)
  621. {
  622.     return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link");
  623. }
  624.  
  625. /* Write and release one node of the outline tree. */
  626. private int
  627. pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode,
  628.               long next_id)
  629. {
  630.     stream *s;
  631.  
  632.     pdf_open_separate(pdev, pnode->id);
  633.     s = pdev->strm;
  634.     pputs(s, "<< ");
  635.     cos_dict_elements_write(pnode->action, pdev);
  636.     if (pnode->count)
  637.     pprintd1(s, "/Count %d ", pnode->count);
  638.     pprintld1(s, "/Parent %ld 0 R\n", pnode->parent_id);
  639.     if (pnode->prev_id)
  640.     pprintld1(s, "/Prev %ld 0 R\n", pnode->prev_id);
  641.     if (next_id)
  642.     pprintld1(s, "/Next %ld 0 R\n", next_id);
  643.     if (pnode->first_id)
  644.     pprintld2(s, "/First %ld 0 R /Last %ld 0 R\n",
  645.           pnode->first_id, pnode->last_id);
  646.     pputs(s, ">>\n");
  647.     pdf_end_separate(pdev);
  648.     COS_FREE(pnode->action, "pdfmark_write_outline");
  649.     pnode->action = 0;
  650.     return 0;
  651. }
  652.  
  653. /* Adjust the parent's count when writing an outline node. */
  654. private void
  655. pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)
  656. {
  657.     pdf_outline_level_t *parent = plevel - 1;
  658.     int count = plevel->last.count;
  659.  
  660.     if (count > 0) {
  661.     if (parent->last.count < 0)
  662.         parent->last.count -= count;
  663.     else
  664.         parent->last.count += count;
  665.     }
  666. }
  667.  
  668. /*
  669.  * Close the current level of the outline tree.  Note that if we are at
  670.  * the end of the document, some of the levels may be incomplete if the
  671.  * Count values were incorrect.
  672.  */
  673. int
  674. pdfmark_close_outline(gx_device_pdf * pdev)
  675. {
  676.     int depth = pdev->outline_depth;
  677.     pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
  678.     int code;
  679.  
  680.     if (plevel->last.id) {    /* check for incomplete tree */
  681.     code = pdfmark_write_outline(pdev, &plevel->last, 0);
  682.     if (code < 0)
  683.         return code;
  684.     }
  685.     if (depth > 0) {
  686.     plevel[-1].last.last_id = plevel->last.id;
  687.     pdfmark_adjust_parent_count(plevel);
  688.     --plevel;
  689.     if (plevel->last.count < 0)
  690.         pdev->closed_outline_depth--;
  691.     pdev->outline_depth--;
  692.     }
  693.     return 0;
  694. }
  695.  
  696. /* OUT pdfmark */
  697. private int
  698. pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  699.         const gs_matrix * pctm, const gs_param_string * no_objname)
  700. {
  701.     int depth = pdev->outline_depth;
  702.     pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
  703.     int sub_count = 0;
  704.     uint i;
  705.     pdf_outline_node_t node;
  706.     ao_params_t ao;
  707.     int code;
  708.  
  709.     for (i = 0; i < count; i += 2) {
  710.     const gs_param_string *pair = &pairs[i];
  711.  
  712.     if (pdf_key_eq(pair, "/Count"))
  713.         pdfmark_scan_int(pair + 1, &sub_count);
  714.     }
  715.     if (sub_count != 0 && depth == MAX_OUTLINE_DEPTH - 1)
  716.     return_error(gs_error_limitcheck);
  717.     node.action = cos_dict_alloc(pdev, "pdfmark_OUT");
  718.     if (node.action == 0)
  719.     return_error(gs_error_VMerror);
  720.     ao.pdev = pdev;
  721.     ao.subtype = 0;
  722.     ao.src_pg = -1;
  723.     code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao,
  724.                 true);
  725.     if (code < 0)
  726.     return code;
  727.     if (pdev->outlines_id == 0)
  728.     pdev->outlines_id = pdf_obj_ref(pdev);
  729.     node.id = pdf_obj_ref(pdev);
  730.     node.parent_id =
  731.     (depth == 0 ? pdev->outlines_id : plevel[-1].last.id);
  732.     node.prev_id = plevel->last.id;
  733.     node.first_id = node.last_id = 0;
  734.     node.count = sub_count;
  735.     /* Add this node to the outline at the current level. */
  736.     if (plevel->first.id == 0) {    /* First node at this level. */
  737.     if (depth > 0)
  738.         plevel[-1].last.first_id = node.id;
  739.     node.prev_id = 0;
  740.     plevel->first = node;
  741.     plevel->first.action = 0; /* never used */
  742.     } else {            /* Write out the previous node. */
  743.     if (depth > 0)
  744.         pdfmark_adjust_parent_count(plevel);
  745.     pdfmark_write_outline(pdev, &plevel->last, node.id);
  746.     }
  747.     plevel->last = node;
  748.     plevel->left--;
  749.     if (!pdev->closed_outline_depth)
  750.     pdev->outlines_open++;
  751.     /* If this node has sub-nodes, descend one level. */
  752.     if (sub_count != 0) {
  753.     pdev->outline_depth++;
  754.     ++plevel;
  755.     plevel->left = (sub_count > 0 ? sub_count : -sub_count);
  756.     plevel->first.id = 0;
  757.     plevel->first.action = plevel->last.action = 0;    /* for GC */
  758.     if (sub_count < 0)
  759.         pdev->closed_outline_depth++;
  760.     } else {
  761.     while ((depth = pdev->outline_depth) > 0 &&
  762.            pdev->outline_levels[depth].left == 0
  763.         )
  764.         pdfmark_close_outline(pdev);
  765.     }
  766.     return 0;
  767. }
  768.  
  769. /* Write an article bead. */
  770. private int
  771. pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead)
  772. {
  773.     stream *s;
  774.     char rstr[MAX_RECT_STRING];
  775.  
  776.     pdf_open_separate(pdev, pbead->id);
  777.     s = pdev->strm;
  778.     pprintld3(s, "<</T %ld 0 R/V %ld 0 R/N %ld 0 R",
  779.           pbead->article_id, pbead->prev_id, pbead->next_id);
  780.     if (pbead->page_id != 0)
  781.     pprintld1(s, "/P %ld 0 R", pbead->page_id);
  782.     pdfmark_make_rect(rstr, &pbead->rect);
  783.     pprints1(s, "/R%s>>\n", rstr);
  784.     return pdf_end_separate(pdev);
  785. }
  786.  
  787. /* Finish writing an article, and release its data. */
  788. int
  789. pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part)
  790. {
  791.     pdf_article_t art;
  792.     stream *s;
  793.  
  794.     art = *part;
  795.     if (art.last.id == 0) {
  796.     /* Only one bead in the article. */
  797.     art.first.prev_id = art.first.next_id = art.first.id;
  798.     } else {
  799.     /* More than one bead in the article. */
  800.     art.first.prev_id = art.last.id;
  801.     art.last.next_id = art.first.id;
  802.     pdfmark_write_bead(pdev, &art.last);
  803.     }
  804.     pdfmark_write_bead(pdev, &art.first);
  805.     pdf_open_separate(pdev, art.contents->id);
  806.     s = pdev->strm;
  807.     pprintld1(s, "<</F %ld 0 R/I<<", art.first.id);
  808.     cos_dict_elements_write(art.contents, pdev);
  809.     pputs(s, ">> >>\n");
  810.     return pdf_end_separate(pdev);
  811. }
  812.  
  813. /* ARTICLE pdfmark */
  814. private int
  815. pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  816.         const gs_matrix * pctm, const gs_param_string * no_objname)
  817. {
  818.     gs_memory_t *mem = pdev->pdf_memory;
  819.     gs_param_string title;
  820.     gs_param_string rectstr;
  821.     gs_rect rect;
  822.     long bead_id;
  823.     pdf_article_t *part;
  824.     int code;
  825.  
  826.     if (!pdfmark_find_key("/Title", pairs, count, &title) ||
  827.     !pdfmark_find_key("/Rect", pairs, count, &rectstr)
  828.     )
  829.     return_error(gs_error_rangecheck);
  830.     if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0)
  831.     return code;
  832.     bead_id = pdf_obj_ref(pdev);
  833.  
  834.     /* Find the article with this title, or create one. */
  835.     for (part = pdev->articles; part != 0; part = part->next) {
  836.     const cos_value_t *a_title =
  837.         cos_dict_find(part->contents, (const byte *)"/Title", 6);
  838.  
  839.     if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) &&
  840.         !bytes_compare(a_title->contents.chars.data,
  841.                a_title->contents.chars.size,
  842.                title.data, title.size))
  843.         break;
  844.     }
  845.     if (part == 0) {        /* Create the article. */
  846.     cos_dict_t *contents =
  847.         cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)");
  848.  
  849.     if (contents == 0)
  850.         return_error(gs_error_VMerror);
  851.     part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article,
  852.                    "pdfmark_ARTICLE(article)");
  853.     if (part == 0 || contents == 0) {
  854.         gs_free_object(mem, part, "pdfmark_ARTICLE(article)");
  855.         if (contents)
  856.         COS_FREE(contents, "pdfmark_ARTICLE(contents)");
  857.         return_error(gs_error_VMerror);
  858.     }
  859.     contents->id = pdf_obj_ref(pdev);
  860.     part->next = pdev->articles;
  861.     pdev->articles = part;
  862.     cos_dict_put_string(contents, (const byte *)"/Title", 6,
  863.                 title.data, title.size);
  864.     part->first.id = part->last.id = 0;
  865.     part->contents = contents;
  866.     }
  867.     /*
  868.      * Add the bead to the article.  This is similar to what we do for
  869.      * outline nodes, except that articles have only a page number and
  870.      * not View/Dest.
  871.      */
  872.     if (part->last.id == 0) {
  873.     part->first.next_id = bead_id;
  874.     part->last.id = part->first.id;
  875.     } else {
  876.     part->last.next_id = bead_id;
  877.     pdfmark_write_bead(pdev, &part->last);
  878.     }
  879.     part->last.prev_id = part->last.id;
  880.     part->last.id = bead_id;
  881.     part->last.article_id = part->contents->id;
  882.     part->last.next_id = 0;
  883.     part->last.rect = rect;
  884.     {
  885.     gs_param_string page_string;
  886.     int page = 0;
  887.     uint i;
  888.  
  889.     if (pdfmark_find_key("/Page", pairs, count, &page_string))
  890.         page = pdfmark_page_number(pdev, &page_string);
  891.     part->last.page_id = pdf_page_id(pdev, page);
  892.     for (i = 0; i < count; i += 2) {
  893.         if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page"))
  894.         continue;
  895.         pdfmark_put_pair(part->contents, &pairs[i]);
  896.     }
  897.     }
  898.     if (part->first.id == 0) {    /* This is the first bead of the article. */
  899.     part->first = part->last;
  900.     part->last.id = 0;
  901.     }
  902.     return 0;
  903. }
  904.  
  905. /* DEST pdfmark */
  906. private int
  907. pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  908.          const gs_matrix * pctm, const gs_param_string * objname)
  909. {
  910.     int present;
  911.     char dest[MAX_DEST_STRING];
  912.     gs_param_string key;
  913.     cos_value_t value;
  914.  
  915.     if (!pdfmark_find_key("/Dest", pairs, count, &key) ||
  916.     (present =
  917.      pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count)) < 0
  918.     )
  919.     return_error(gs_error_rangecheck);
  920.     cos_string_value(&value, (byte *)dest, strlen(dest));
  921.     if (!pdev->Dests) {
  922.     pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)");
  923.     if (pdev->Dests == 0)
  924.         return_error(gs_error_VMerror);
  925.     pdev->Dests->id = pdf_obj_ref(pdev);
  926.     }
  927.     if (objname || count > (present + 1) * 2) {
  928.     /*
  929.      * Create the destination as a dictionary with a D key, since
  930.      * it has (or, if named, may have) additional key/value pairs.
  931.      */
  932.     cos_dict_t *ddict;
  933.     int i, code;
  934.  
  935.     code = pdf_make_named_dict(pdev, objname, &ddict, false);
  936.     if (code < 0)
  937.         return code;
  938.     code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest,
  939.                      strlen(dest));
  940.     for (i = 0; code >= 0 && i < count; i += 2)
  941.         if (!pdf_key_eq(&pairs[i], "/Dest") &&
  942.         !pdf_key_eq(&pairs[i], "/Page") &&
  943.         !pdf_key_eq(&pairs[i], "/View")
  944.         )
  945.         code = pdfmark_put_pair(ddict, &pairs[i]);
  946.     if (code < 0)
  947.         return code;
  948.     COS_OBJECT_VALUE(&value, ddict);
  949.     }
  950.     return cos_dict_put(pdev->Dests, key.data, key.size, &value);
  951. }
  952.  
  953. /* Check that pass-through PostScript code is a string. */
  954. private bool
  955. ps_source_ok(const gs_param_string * psource)
  956. {
  957.     if (psource->size >= 2 && psource->data[0] == '(' &&
  958.     psource->data[psource->size - 1] == ')'
  959.     )
  960.     return true;
  961.     else {
  962.     lprintf("bad PS passthrough: ");
  963.     fwrite(psource->data, 1, psource->size, estderr);
  964.     fputs("\n", estderr);
  965.     return false;
  966.     }
  967. }
  968.  
  969. /* Write the contents of pass-through PostScript code. */
  970. /* Return the size written on the file. */
  971. private uint
  972. pdfmark_write_ps(stream *s, const gs_param_string * psource)
  973. {
  974.     /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/
  975.     uint size = psource->size - 2;
  976.  
  977.     pwrite(s, psource->data + 1, size);
  978.     pputc(s, '\n');
  979.     return size + 1;
  980. }
  981.  
  982. /* PS pdfmark */
  983. #define MAX_PS_INLINE 100
  984. private int
  985. pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  986.        const gs_matrix * pctm, const gs_param_string * objname)
  987. {
  988.     gs_param_string source;
  989.     gs_param_string level1;
  990.  
  991.     if (!pdfmark_find_key("/DataSource", pairs, count, &source) ||
  992.     !ps_source_ok(&source) ||
  993.     (pdfmark_find_key("/Level1", pairs, count, &level1) &&
  994.      !ps_source_ok(&level1))
  995.     )
  996.     return_error(gs_error_rangecheck);
  997.     if (level1.data == 0 && source.size <= MAX_PS_INLINE &&
  998.     pdev->CompatibilityLevel >= 1.2 && objname == 0
  999.     ) {
  1000.     /* Insert the PostScript code in-line */
  1001.     int code = pdf_open_contents(pdev, PDF_IN_STREAM);
  1002.     stream *s;
  1003.  
  1004.     if (code < 0)
  1005.         return code;
  1006.     s = pdev->strm;
  1007.     pwrite(s, source.data, source.size);
  1008.     pputs(s, " PS\n");
  1009.     } else {
  1010.     /* Put the PostScript code in a resource. */
  1011.     pdf_resource_t *pres;
  1012.     cos_stream_t *pcs;
  1013.     uint size;
  1014.     int code;
  1015.  
  1016.     code = pdf_make_named(pdev, objname, cos_type_stream,
  1017.                   (cos_object_t **)&pcs, true);
  1018.     if (code < 0)
  1019.         return code;
  1020.     code = pdf_alloc_resource(pdev, resourceXObject, gs_no_id, &pres,
  1021.                   pcs->id);
  1022.     if (code < 0)
  1023.         return code;
  1024.     pres->object = COS_OBJECT(pcs);
  1025.     code = cos_stream_put_c_strings(pcs, "/Type", "/XObject");
  1026.     if (code < 0)
  1027.         return code;
  1028.     code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS");
  1029.     if (code < 0)
  1030.         return code;
  1031.     if (level1.data != 0) {
  1032.         long level1_id = pdf_obj_ref(pdev);
  1033.         char r[10 + 5];    /* %ld 0 R\0 */
  1034.         stream *s;
  1035.         long length_id = pdf_obj_ref(pdev);
  1036.  
  1037.         sprintf(r, "%ld 0 R", level1_id);
  1038.         code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1",
  1039.                          (byte *)r, strlen(r));
  1040.         if (code < 0)
  1041.         return code;
  1042.         pdf_open_separate(pdev, level1_id);
  1043.         s = pdev->strm;
  1044.         pprintld1(s, "<</Length %ld 0 R>>stream\n", length_id);
  1045.         size = pdfmark_write_ps(s, &level1);
  1046.         pputs(s, "endstream\n");
  1047.         pdf_end_separate(pdev);
  1048.         pdf_open_separate(pdev, length_id);
  1049.         pprintld1(s, "%ld\n", (long)size);
  1050.         pdf_end_separate(pdev);
  1051.     }
  1052.     size = pdfmark_write_ps(pdev->streams.strm, &source);
  1053.     code = cos_stream_add(pcs, size);
  1054.     if (code < 0)
  1055.         return code;
  1056.     if (objname)
  1057.         pres->named = true;
  1058.     else {
  1059.         /* Write the resource now, since it won't be written later. */
  1060.         COS_WRITE_OBJECT(pcs, pdev);
  1061.         COS_RELEASE(pcs, "pdfmark_PS");
  1062.     }
  1063.     code = pdf_open_contents(pdev, PDF_IN_STREAM);
  1064.     if (code < 0)
  1065.         return code;
  1066.     pprintld1(pdev->strm, "/R%ld Do\n", pcs->id);
  1067.     }
  1068.     return 0;
  1069. }
  1070.  
  1071. /* Common code for pdfmarks that do PUT into a specific object. */
  1072. private int
  1073. pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count)
  1074. {
  1075.     int code = 0, i;
  1076.  
  1077.     if (count & 1)
  1078.     return_error(gs_error_rangecheck);
  1079.     for (i = 0; code >= 0 && i < count; i += 2)
  1080.     code = pdfmark_put_pair(pcd, pairs + i);
  1081.     return code;
  1082. }
  1083.  
  1084. /* PAGES pdfmark */
  1085. private int
  1086. pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1087.           const gs_matrix * pctm, const gs_param_string * no_objname)
  1088. {
  1089.     return pdfmark_put_pairs(pdev->Pages, pairs, count);
  1090. }
  1091.  
  1092. /* PAGE pdfmark */
  1093. private int
  1094. pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1095.          const gs_matrix * pctm, const gs_param_string * no_objname)
  1096. {
  1097.     return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count);
  1098. }
  1099.  
  1100. /* DOCINFO pdfmark */
  1101. private int
  1102. pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1103.         const gs_matrix * pctm, const gs_param_string * no_objname)
  1104. {
  1105.     /*
  1106.      * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except
  1107.      * that we want to replace "Distiller" with our own name as the
  1108.      * Producer.
  1109.      */
  1110.     cos_dict_t *const pcd = pdev->Info;
  1111.     int code = 0, i;
  1112.     gs_memory_t *mem = pdev->memory;
  1113.  
  1114.     if (count & 1)
  1115.     return_error(gs_error_rangecheck);
  1116.     for (i = 0; code >= 0 && i < count; i += 2) {
  1117.     const gs_param_string *pair = pairs + i;
  1118.     gs_param_string alt_pair[2];
  1119. #define VDATA alt_pair[1].data
  1120. #define VSIZE alt_pair[1].size
  1121.     byte *str = 0;
  1122.  
  1123.     if (pdf_key_eq(pairs + i, "/Producer")) {
  1124.         /*
  1125.          * If the string "Distiller" appears anywhere in the Producer,
  1126.          * replace the Producer (or the part after a " + ") with our
  1127.          * own name.
  1128.          */
  1129.         string_match_params params;
  1130.  
  1131.         memcpy(alt_pair, pairs + i, sizeof(alt_pair));
  1132.         params = string_match_params_default;
  1133.         params.ignore_case = true;
  1134.         if (string_match(VDATA, VSIZE, (const byte *)"*Distiller*",
  1135.                  11, ¶ms) ||
  1136.         string_match(VDATA, VSIZE,
  1137.          (const byte *)"*\000D\000i\000s\000t\000i\000l\000l\000e\000r*",
  1138.                  20, ¶ms)
  1139.         ) {
  1140.         uint j;
  1141.         char buf[PDF_MAX_PRODUCER];
  1142.         int len;
  1143.  
  1144.         for (j = VSIZE; j > 0 && VDATA[--j] != '+'; )
  1145.             DO_NOTHING;
  1146.         if (VSIZE - j > 2 && VDATA[j] == '+') {
  1147.             ++j;
  1148.             while (j < VSIZE && VDATA[j] == ' ')
  1149.             ++j;
  1150.         }
  1151.         /*
  1152.          * Replace VDATA[j .. VSIZE) with our name.  Note that both
  1153.          * VDATA/VSTR and the default producer string are enclosed
  1154.          * in ().
  1155.          */
  1156.         pdf_store_default_Producer(buf);
  1157.         len = strlen(buf) - 1;
  1158.         str = gs_alloc_string(mem, j + len, "Producer");
  1159.         if (str == 0)
  1160.             return_error(gs_error_VMerror);
  1161.         memcpy(str, VDATA, j);
  1162.         memcpy(str + j, buf + 1, len);
  1163.         VDATA = str;
  1164.         VSIZE = j + len;
  1165.         pair = alt_pair;
  1166.         }
  1167.     }
  1168.     code = pdfmark_put_pair(pcd, pair);
  1169.     if (str)
  1170.         gs_free_string(mem, str, VSIZE, "Producer");
  1171. #undef VDATA
  1172. #undef VSIZE
  1173.     }
  1174.     return code;
  1175. }
  1176.  
  1177. /* DOCVIEW pdfmark */
  1178. private int
  1179. pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1180.         const gs_matrix * pctm, const gs_param_string * no_objname)
  1181. {
  1182.     char dest[MAX_DEST_STRING];
  1183.     int code = 0;
  1184.  
  1185.     if (count & 1)
  1186.     return_error(gs_error_rangecheck);
  1187.     if (pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count)) {
  1188.     int i;
  1189.  
  1190.     code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction",
  1191.                      (byte *)dest, strlen(dest));
  1192.     for (i = 0; code >= 0 && i < count; i += 2)
  1193.         if (!(pdf_key_eq(&pairs[i], "/Page") ||
  1194.           pdf_key_eq(&pairs[i], "/View"))
  1195.         )
  1196.         code = pdfmark_put_pair(pdev->Catalog, pairs + i);
  1197.     return code;
  1198.     } else
  1199.     return pdfmark_put_pairs(pdev->Catalog, pairs, count);
  1200. }
  1201.  
  1202. /* ---------------- Named object pdfmarks ---------------- */
  1203.  
  1204. /* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */
  1205. private int
  1206. pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1207.        const gs_matrix * pctm, const gs_param_string * objname)
  1208. {
  1209.     gs_rect bbox;
  1210.     cos_stream_t *pcs;
  1211.     pdf_graphics_save_t *pdgs;
  1212.     int code;
  1213.     double xscale = pdev->HWResolution[0] / 72.0,
  1214.     yscale = pdev->HWResolution[1] / 72.0;
  1215.     char bbox_str[6 + 4 * 12];
  1216.  
  1217.     if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox"))
  1218.     return_error(gs_error_rangecheck);
  1219.     if (sscanf((const char *)pairs[1].data, "[%lg %lg %lg %lg]",
  1220.            &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4
  1221.     )
  1222.     return_error(gs_error_rangecheck);
  1223.     code = pdf_make_named(pdev, objname, cos_type_stream,
  1224.               (cos_object_t **)&pcs, true);
  1225.     if (code < 0)
  1226.     return code;
  1227.     pcs->is_graphics = true;
  1228.     gs_bbox_transform(&bbox, pctm, &bbox);
  1229.     sprintf(bbox_str, "[%.8g %.8g %.8g %.8g]",
  1230.         bbox.p.x * xscale, bbox.p.y * yscale,
  1231.         bbox.q.x * xscale, bbox.q.y * yscale);
  1232.     if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 ||
  1233.     (code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 ||
  1234.     (code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 ||
  1235.     (code = cos_stream_put_c_strings(pcs, "/Matrix", "[1 0 0 1 0 0]")) < 0 ||
  1236.     (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox",
  1237.                       (byte *)bbox_str,
  1238.                       strlen(bbox_str))) < 0
  1239.     )
  1240.     return code;
  1241.     pdgs = gs_alloc_struct(pdev->pdf_memory, pdf_graphics_save_t,
  1242.                &st_pdf_graphics_save, "pdfmark_BP");
  1243.     if (pdgs == 0)
  1244.     return_error(gs_error_VMerror);
  1245.     if (pdev->context != PDF_IN_NONE) {
  1246.     code = pdf_open_page(pdev, PDF_IN_STREAM);
  1247.     if (code < 0) {
  1248.         gs_free_object(pdev->pdf_memory, pdgs, "pdfmark_BP");
  1249.         return code;
  1250.     }
  1251.     }
  1252.     if (!pdev->open_graphics) {
  1253.     pdev->pictures.save_strm = pdev->strm;
  1254.     pdev->strm = pdev->pictures.strm;
  1255.     }
  1256.     pdgs->prev = pdev->open_graphics;
  1257.     pdgs->object = pcs;
  1258.     pdgs->position = stell(pdev->pictures.strm);
  1259.     pdgs->save_context = pdev->context;
  1260.     pdgs->save_contents_id = pdev->contents_id;
  1261.     pdev->open_graphics = pdgs;
  1262.     pdev->context = PDF_IN_STREAM;
  1263.     pdev->contents_id = pcs->id;
  1264.     return 0;
  1265. }
  1266.  
  1267. /* [ /EP pdfmark */
  1268. private int
  1269. pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1270.        const gs_matrix * pctm, const gs_param_string * no_objname)
  1271. {
  1272.     pdf_graphics_save_t *pdgs = pdev->open_graphics;
  1273.     pdf_resource_t *pres;
  1274.     cos_stream_t *pcs;
  1275.     long start;
  1276.     uint size;
  1277.     int code;
  1278.  
  1279.     if (count != 0 || pdgs == 0 || !(pcs = pdgs->object)->is_open)
  1280.     return_error(gs_error_rangecheck);
  1281.     code = pdf_open_contents(pdev, PDF_IN_STREAM);
  1282.     if (code < 0)
  1283.     return code;
  1284.     code = pdf_alloc_resource(pdev, resourceXObject, gs_no_id, &pres,
  1285.                   pcs->id);
  1286.     if (code < 0)
  1287.     return code;
  1288.     pres->object = COS_OBJECT(pcs);
  1289.     start = pdgs->position;
  1290.     pcs->is_open = false;
  1291.     size = stell(pdev->strm) - start;
  1292.     pdev->open_graphics = pdgs->prev;
  1293.     pdev->context = pdgs->save_context;
  1294.     pdev->contents_id = pdgs->save_contents_id;
  1295.     gs_free_object(pdev->pdf_memory, pdgs, "pdfmark_EP");
  1296.     /* Copy the data to the streams file. */
  1297.     sflush(pdev->strm);
  1298.     sseek(pdev->strm, start);
  1299.     fseek(pdev->pictures.file, start, SEEK_SET);
  1300.     pdf_copy_data(pdev->streams.strm, pdev->pictures.file, size);
  1301.     code = cos_stream_add(pcs, size);
  1302.     /* Keep the file in sync with the stream. */
  1303.     fseek(pdev->pictures.file, start, SEEK_SET);
  1304.     if (!pdev->open_graphics) {
  1305.     pdev->strm = pdev->pictures.save_strm;
  1306.     pdev->pictures.save_strm = 0;
  1307.     }
  1308.     return code;
  1309. }
  1310.  
  1311. /* [ {obj} /SP pdfmark */
  1312. private int
  1313. pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1314.        const gs_matrix * pctm, const gs_param_string * no_objname)
  1315. {
  1316.     cos_object_t *pco;        /* stream */
  1317.     gs_matrix ctm;
  1318.     int code;
  1319.  
  1320.     if (count != 1)
  1321.     return_error(gs_error_rangecheck);
  1322.     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
  1323.     return code;
  1324.     if (pco->is_open || !pco->is_graphics)
  1325.     return_error(gs_error_rangecheck);
  1326.     code = pdf_open_contents(pdev, PDF_IN_STREAM);
  1327.     if (code < 0)
  1328.     return code;
  1329.     ctm = *pctm;
  1330.     ctm.tx *= pdev->HWResolution[0] / 72.0;
  1331.     ctm.ty *= pdev->HWResolution[1] / 72.0;
  1332.     pdf_put_matrix(pdev, "q ", &ctm, "cm\n");
  1333.     pprintld1(pdev->strm, "/R%ld Do Q\n", pco->id);
  1334.     return 0;
  1335. }
  1336.  
  1337. /* [ /_objdef {array} /type /array /OBJ pdfmark */
  1338. /* [ /_objdef {dict} /type /dict /OBJ pdfmark */
  1339. /* [ /_objdef {stream} /type /stream /OBJ pdfmark */
  1340. private int
  1341. pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1342.         const gs_matrix * pctm, const gs_param_string * objname)
  1343. {
  1344.     cos_type_t cotype;
  1345.     cos_object_t *pco;
  1346.     int code;
  1347.  
  1348.     if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type"))
  1349.     return_error(gs_error_rangecheck);
  1350.     if (pdf_key_eq(&pairs[1], "/array"))
  1351.     cotype = cos_type_array;
  1352.     else if (pdf_key_eq(&pairs[1], "/dict"))
  1353.     cotype = cos_type_dict;
  1354.     else if (pdf_key_eq(&pairs[1], "/stream"))
  1355.     cotype = cos_type_stream;
  1356.     else
  1357.     return_error(gs_error_rangecheck);
  1358.     if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) {
  1359.     /*
  1360.      * For Distiller compatibility, allows multiple /OBJ pdfmarks with
  1361.      * the same name and type, even though the pdfmark specification
  1362.      * doesn't say anything about this being legal.
  1363.      */
  1364.     if (code == gs_error_rangecheck &&
  1365.         pdf_refer_named(pdev, objname, &pco) >= 0 &&
  1366.         cos_type(pco) == cotype
  1367.         )
  1368.         return 0;        /* already exists, but OK */
  1369.     return code;
  1370.     }
  1371.     return 0;
  1372. }
  1373.  
  1374. /* [ {array} index value /PUT pdfmark */
  1375. /* Dictionaries are converted to .PUTDICT */
  1376. /* Streams are converted to .PUTSTREAM */
  1377. private int
  1378. pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1379.         const gs_matrix * pctm, const gs_param_string * no_objname)
  1380. {
  1381.     cos_object_t *pco;
  1382.     cos_value_t value;
  1383.     int code, index;
  1384.  
  1385.     if (count != 3)
  1386.     return_error(gs_error_rangecheck);
  1387.     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
  1388.     return code;
  1389.     if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
  1390.     return code;
  1391.     if (index < 0)
  1392.     return_error(gs_error_rangecheck);
  1393.     return cos_array_put((cos_array_t *)pco, index,
  1394.         cos_string_value(&value, pairs[2].data, pairs[2].size));
  1395. }
  1396.  
  1397. /* [ {dict} key value ... /.PUTDICT pdfmark */
  1398. /* [ {stream} key value ... /.PUTDICT pdfmark */
  1399. /*
  1400.  * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream,
  1401.  * but it's reasonable and unambiguous, and Acrobat Distiller accepts it,
  1402.  * so we do too.
  1403.  */
  1404. private int
  1405. pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1406.         const gs_matrix * pctm, const gs_param_string * no_objname)
  1407. {
  1408.     cos_object_t *pco;
  1409.     int code;
  1410.  
  1411.     if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
  1412.     return code;
  1413.     if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream)
  1414.     return_error(gs_error_typecheck);
  1415.     return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1);
  1416. }
  1417.  
  1418. /* [ {stream} string ... /.PUTSTREAM pdfmark */
  1419. private int
  1420. pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1421.           const gs_matrix * pctm, const gs_param_string * no_objname)
  1422. {
  1423.     cos_object_t *pco;
  1424.     int code, i;
  1425.  
  1426.     if (count < 2)
  1427.     return_error(gs_error_rangecheck);
  1428.     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
  1429.     return code;
  1430.     if (!pco->is_open)
  1431.     return_error(gs_error_rangecheck);
  1432.     for (i = 1; code >= 0 && i < count; ++i)
  1433.     code = cos_stream_add_bytes((cos_stream_t *)pco, pairs[i].data,
  1434.                     pairs[i].size);
  1435.     return code;
  1436. }
  1437.  
  1438. /* [ {array} index value ... /.PUTINTERVAL pdfmark */
  1439. private int
  1440. pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1441.          const gs_matrix * pctm, const gs_param_string * no_objname)
  1442. {
  1443.     cos_object_t *pco;
  1444.     cos_value_t value;
  1445.     int code, index, i;
  1446.  
  1447.     if (count < 2)
  1448.     return_error(gs_error_rangecheck);
  1449.     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
  1450.     return code;
  1451.     if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
  1452.     return code;
  1453.     if (index < 0)
  1454.     return_error(gs_error_rangecheck);
  1455.     for (i = 2; code >= 0 && i < count; ++i)
  1456.     code = cos_array_put((cos_array_t *)pco, index + i - 2,
  1457.         cos_string_value(&value, pairs[i].data, pairs[i].size));
  1458.     return code;
  1459. }
  1460.  
  1461. /* [ {stream} /CLOSE pdfmark */
  1462. private int
  1463. pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
  1464.           const gs_matrix * pctm, const gs_param_string * no_objname)
  1465. {
  1466.     cos_object_t *pco;
  1467.     int code;
  1468.  
  1469.     if (count != 1)
  1470.     return_error(gs_error_rangecheck);
  1471.     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
  1472.     return code;
  1473.     if (!pco->is_open)
  1474.     return_error(gs_error_rangecheck);
  1475.     /* Currently we don't do anything special when closing a stream. */
  1476.     pco->is_open = false;
  1477.     return 0;
  1478. }
  1479.  
  1480. /* [ /NamespacePush pdfmark */
  1481. private int
  1482. pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1483.               const gs_matrix *pctm, const gs_param_string *objname)
  1484. {
  1485.     return 0;
  1486. }
  1487.  
  1488. /* [ /NamespacePop pdfmark */
  1489. private int
  1490. pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1491.              const gs_matrix *pctm, const gs_param_string *objname)
  1492. {
  1493.     return 0;
  1494. }
  1495.  
  1496. /* [ /_objdef {image} /NI pdfmark */
  1497. private int
  1498. pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1499.        const gs_matrix *pctm, const gs_param_string *objname)
  1500. {
  1501.     return 0;
  1502. }
  1503.  
  1504. /* ---------------- Document structure pdfmarks ---------------- */
  1505.  
  1506. /* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */
  1507. private int
  1508. pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1509.           const gs_matrix *pctm, const gs_param_string *objname)
  1510. {
  1511.     return 0;
  1512. }
  1513.  
  1514. /* [ class1 {attrobj1} ... /StClassMap pdfmark */
  1515. private int
  1516. pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1517.            const gs_matrix *pctm, const gs_param_string *objname)
  1518. {
  1519.     return 0;
  1520. }
  1521.  
  1522. /*
  1523.  * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string]
  1524.  *   [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...]
  1525.  *   /StPNE pdfmark
  1526.  */
  1527. private int
  1528. pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1529.           const gs_matrix *pctm, const gs_param_string *objname)
  1530. {
  1531.     return 0;
  1532. }
  1533.  
  1534. /* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */
  1535. private int
  1536. pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1537.                const gs_matrix *pctm, const gs_param_string *objname)
  1538. {
  1539.     return 0;
  1540. }
  1541.  
  1542. /* [ [/E {elt}] /StPush pdfmark */
  1543. private int
  1544. pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1545.            const gs_matrix *pctm, const gs_param_string *objname)
  1546. {
  1547.      return 0;
  1548. }
  1549.  
  1550. /* [ /StPop pdfmark */
  1551. private int
  1552. pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1553.           const gs_matrix *pctm, const gs_param_string *objname)
  1554. {
  1555.     return 0;
  1556. }
  1557.  
  1558. /* [ /StPopAll pdfmark */
  1559. private int
  1560. pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1561.          const gs_matrix *pctm, const gs_param_string *objname)
  1562. {
  1563.     return 0;
  1564. }
  1565.  
  1566. /* [ [/T tagname] [/At index] /StBMC pdfmark */
  1567. private int
  1568. pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1569.           const gs_matrix *pctm, const gs_param_string *objname)
  1570. {
  1571.     return 0;
  1572. }
  1573.  
  1574. /* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */
  1575. private int
  1576. pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1577.           const gs_matrix *pctm, const gs_param_string *objname)
  1578. {
  1579.     return 0;
  1580. }
  1581.  
  1582. /* [ /EMC pdfmark */
  1583. private int
  1584. pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1585.         const gs_matrix *pctm, const gs_param_string *objname)
  1586. {
  1587.     return 0;
  1588. }
  1589.  
  1590. /* [ /Obj {obj} [/At index] /StOBJ pdfmark */
  1591. private int
  1592. pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1593.           const gs_matrix *pctm, const gs_param_string *objname)
  1594. {
  1595.     return 0;
  1596. }
  1597.  
  1598. /* [ /Obj {obj} /StAttr pdfmark */
  1599. private int
  1600. pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1601.            const gs_matrix *pctm, const gs_param_string *objname)
  1602. {
  1603.     return 0;
  1604. }
  1605.  
  1606. /* [ /StoreName name /StStore pdfmark */
  1607. private int
  1608. pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1609.         const gs_matrix *pctm, const gs_param_string *objname)
  1610. {
  1611.     return 0;
  1612. }
  1613.  
  1614. /* [ /StoreName name /StRetrieve pdfmark */
  1615. private int
  1616. pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
  1617.            const gs_matrix *pctm, const gs_param_string *objname)
  1618. {
  1619.     return 0;
  1620. }
  1621.  
  1622. /* ---------------- Dispatch ---------------- */
  1623.  
  1624. /*
  1625.  * Define the pdfmark types we know about.
  1626.  */
  1627. private const pdfmark_name mark_names[] =
  1628. {
  1629.     /* Miscellaneous. */
  1630.     {"ANN",          pdfmark_ANN,         PDFMARK_NAMEABLE},
  1631.     {"LNK",          pdfmark_LNK,         PDFMARK_NAMEABLE},
  1632.     {"OUT",          pdfmark_OUT,         0},
  1633.     {"ARTICLE",      pdfmark_ARTICLE,     0},
  1634.     {"DEST",         pdfmark_DEST,        PDFMARK_NAMEABLE},
  1635.     {"PS",           pdfmark_PS,          PDFMARK_NAMEABLE},
  1636.     {"PAGES",        pdfmark_PAGES,       0},
  1637.     {"PAGE",         pdfmark_PAGE,        0},
  1638.     {"DOCINFO",      pdfmark_DOCINFO,     0},
  1639.     {"DOCVIEW",      pdfmark_DOCVIEW,     0},
  1640.     /* Named objects. */
  1641.     {"BP",           pdfmark_BP,          PDFMARK_NAMEABLE},
  1642.     {"EP",           pdfmark_EP,          0},
  1643.     {"SP",           pdfmark_SP,          PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
  1644.     {"OBJ",          pdfmark_OBJ,         PDFMARK_NAMEABLE},
  1645.     {"PUT",          pdfmark_PUT,         PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
  1646.     {".PUTDICT",     pdfmark_PUTDICT,     PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
  1647.     {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
  1648.     {".PUTSTREAM",   pdfmark_PUTSTREAM,   PDFMARK_ODD_OK | PDFMARK_KEEP_NAME |
  1649.                                           PDFMARK_NO_REFS},
  1650.     {"CLOSE",        pdfmark_CLOSE,       PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
  1651.     {"NamespacePush", pdfmark_NamespacePush, 0},
  1652.     {"NamespacePop", pdfmark_NamespacePop, 0},
  1653.     {"NI",           pdfmark_NI,          PDFMARK_NAMEABLE},
  1654.     /* Document structure. */
  1655.     {"StRoleMap",    pdfmark_StRoleMap,   0},
  1656.     {"StClassMap",   pdfmark_StClassMap,  0},
  1657.     {"StPNE",        pdfmark_StPNE,       PDFMARK_NAMEABLE},
  1658.     {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0},
  1659.     {"StPush",       pdfmark_StPush,       0},
  1660.     {"StPop",        pdfmark_StPop,        0},
  1661.     {"StPopAll",     pdfmark_StPopAll,     0},
  1662.     {"StBMC",        pdfmark_StBMC,        0},
  1663.     {"StBDC",        pdfmark_StBDC,        0},
  1664.     {"EMC",          pdfmark_EMC,          0},
  1665.     {"StOBJ",        pdfmark_StOBJ,        0},
  1666.     {"StAttr",       pdfmark_StAttr,       0},
  1667.     {"StStore",      pdfmark_StStore,      0},
  1668.     {"StRetrieve",   pdfmark_StRetrieve,   0},
  1669.     /* End of list. */
  1670.     {0, 0}
  1671. };
  1672.  
  1673. /* Process a pdfmark. */
  1674. int
  1675. pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
  1676. {
  1677.     const gs_param_string *data = pma->data;
  1678.     uint size = pma->size;
  1679.     const gs_param_string *pts = &data[size - 1];
  1680.     const gs_param_string *objname = 0;
  1681.     gs_matrix ctm;
  1682.     const pdfmark_name *pmn;
  1683.     int code = 0;
  1684.  
  1685.     if (size < 2 ||
  1686.     sscanf((const char *)pts[-1].data, "[%g %g %g %g %g %g]",
  1687.            &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty) != 6
  1688.     )
  1689.     return_error(gs_error_rangecheck);
  1690.     /*
  1691.      * Our coordinate system is scaled so that user space is always
  1692.      * default user space.  Adjust the CTM to match this.
  1693.      */
  1694.     {
  1695.     double xscale = 72.0 / pdev->HWResolution[0],
  1696.         yscale = 72.0 / pdev->HWResolution[1];
  1697.  
  1698.     ctm.xx *= xscale, ctm.xy *= yscale;
  1699.     ctm.yx *= xscale, ctm.yy *= yscale;
  1700.     ctm.tx *= xscale, ctm.ty *= yscale;
  1701.     }
  1702.     size -= 2;            /* remove CTM & pdfmark name */
  1703.     for (pmn = mark_names; pmn->mname != 0; ++pmn)
  1704.     if (pdf_key_eq(pts, pmn->mname)) {
  1705.         gs_memory_t *mem = pdev->pdf_memory;
  1706.         int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0;
  1707.         gs_param_string *pairs;
  1708.         int j;
  1709.  
  1710.         if (size & !odd_ok)
  1711.         return_error(gs_error_rangecheck);
  1712.         if (pmn->options & PDFMARK_NAMEABLE) {
  1713.         /* Look for an object name. */
  1714.         for (j = 0; j < size; j += 2) {
  1715.             if (pdf_key_eq(&data[j], "/_objdef")) {
  1716.             objname = &data[j + 1];
  1717.             if (!pdf_objname_is_valid(objname->data,
  1718.                           objname->size)
  1719.                 )
  1720.                 return_error(gs_error_rangecheck);
  1721.             /* Save the pairs without the name. */
  1722.             size -= 2;
  1723.             pairs = (gs_param_string *)
  1724.                 gs_alloc_byte_array(mem, size,
  1725.                         sizeof(gs_param_string),
  1726.                         "pdfmark_process(pairs)");
  1727.             if (!pairs)
  1728.                 return_error(gs_error_VMerror);
  1729.             memcpy(pairs, data, j * sizeof(*data));
  1730.             memcpy(pairs + j, data + j + 2,
  1731.                    (size - j) * sizeof(*data));
  1732.             goto copied;
  1733.             }
  1734.         }
  1735.         }
  1736.         /* Save all the pairs. */
  1737.         pairs = (gs_param_string *)
  1738.         gs_alloc_byte_array(mem, size, sizeof(gs_param_string),
  1739.                     "pdfmark_process(pairs)");
  1740.         if (!pairs)
  1741.         return_error(gs_error_VMerror);
  1742.         memcpy(pairs, data, size * sizeof(*data));
  1743.     copied:        /* Substitute object references for names. */
  1744.         if (!(pmn->options & PDFMARK_NO_REFS)) {
  1745.         for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok);
  1746.              j < size; j += 2 - odd_ok
  1747.              ) {
  1748.             code = pdf_replace_names(pdev, &pairs[j], &pairs[j]);
  1749.             if (code < 0) {
  1750.             gs_free_object(mem, pairs, "pdfmark_process(pairs)");
  1751.             return code;
  1752.             }
  1753.         }
  1754.         }
  1755.         code = (*pmn->proc) (pdev, pairs, size, &ctm, objname);
  1756.         gs_free_object(mem, pairs, "pdfmark_process(pairs)");
  1757.         break;
  1758.     }
  1759.     return code;
  1760. }
  1761.